package com.tarlog.maven.plugins;

/*
 * Copyright 2011 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.apache.tools.ant.DirectoryScanner;

/**
 * @goal replace
 * @phase install 
 */
public class ReplaceTargetMojo extends AbstractMojo {

    /**
     * @parameter expression="${replaceDirectory}"
     */
    private File           replaceDirectory;

    /**
     * @parameter 
     */
    private File[]         replaceDirectories;

    /**
     * @parameter expression="${targetFile}" default-value="${project.build.finalName}.${project.packaging}"
     */
    private String         targetFileName;

    /**
     * @parameter
     */
    private String[]       includePackaging;

    /** 
     * @parameter default-value="${project}" 
     * 
     */
    private MavenProject   mavenProject;

    /**
     * @parameter default-value="${project.build.directory}"
     */
    private String         buildDirectory;

    /**
     * @parameter expression="${freplace}" default-value="false"
     */
    private boolean        defaultExecution;

    /**
     * @parameter
     */
    private FileNameConf[] fileNameConfs;

    public void execute() throws MojoExecutionException {
        try {
            Log log = getLog();

            // execute only if defaultExecution is true
            // it can be true either by configuration or using system property
            if (!defaultExecution) {
                log.debug("Execution skipped. Either run with -Dfreplace=true, or put defaultExecution=true");
                return;
            }

            // if includePackaging is not specified, default is "jar"
            if (includePackaging == null || includePackaging.length == 0) {
                includePackaging = new String[] { "jar" };
            }

            // replacement can be done either is single or multiple directories
            if (replaceDirectories == null || replaceDirectories.length == 0) {
                if (replaceDirectory != null) {
                    replaceDirectories = new File[] { replaceDirectory };
                } else {
                    log.warn("replaceDirectories or replaceDirectory must be specified. Doing nothing");
                    return;
                }
            }

            // special file name mapping can be defined
            if (fileNameConfs != null && fileNameConfs.length > 0) {
                FileNameConf fileNameConf = null;
                fnc:
                for (FileNameConf conf : fileNameConfs) {
                    if (conf.getTargetFileName().equals(targetFileName)) {
                        fileNameConf = conf;
                        break fnc;
                    }
                }
                if (fileNameConf != null) {
                    String mapping = fileNameConf.getMapFileName();
                    if (mapping != null) {
                        log.info("File name mapping: " + targetFileName + " was replaced with "
                            + mapping);
                        targetFileName = mapping;
                    }
                    File[] removeFiles = fileNameConf.getRemoveFiles();
                    if (removeFiles != null && removeFiles.length > 0) {
                        for (File rf : removeFiles) {
                            log.info("Deleting " + rf.toString());
                            FileUtils.deleteDirectory(rf);
                        }
                    }
                }
            }


            String packaging = mavenProject.getPackaging();
            boolean packagingFound = false;
            for (String p : includePackaging) {
                if (p.equals(packaging)) {
                    packagingFound = true;
                    break;
                }
            }

            if (!packagingFound) {
                log.debug("Packaging " + packaging + " ignored");
                return;
            }

            // start replacement
            for (File replaceDirectory : replaceDirectories) {
                log.debug("Replace in the directory: " + replaceDirectory.toString());
                log.debug("Replacing file: " + targetFileName);

                DirectoryScanner scanner = new DirectoryScanner();
                scanner.setIncludes(new String[] { "**/" + targetFileName });
                scanner.setBasedir(replaceDirectory);
                scanner.setCaseSensitive(false);
                scanner.scan();
                String[] files = scanner.getIncludedFiles();
                File targetFile = new File(buildDirectory + "/"
                    + mavenProject.getBuild().getFinalName() + "." + mavenProject.getPackaging());

                if (files != null && files.length > 0) {
                    for (String f : files) {
                        File file = new File(replaceDirectory + "/" + f);
                        copyFile(targetFile, file);
                    }
                } else {
                    log.debug("No files to replace in " + replaceDirectory.toString() + " found");
                }
            }
        }
        catch (Exception e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

    private void copyFile(File sourceFile, File destFile) throws MojoExecutionException {
        try {
            getLog().info("Replacing " + destFile + " with " + sourceFile);
            if (!destFile.exists()) {
                destFile.createNewFile();
            }

            FileChannel source = null;
            FileChannel destination = null;
            try {
                source = new FileInputStream(sourceFile).getChannel();
                destination = new FileOutputStream(destFile).getChannel();
                destination.transferFrom(source, 0, source.size());
            }
            finally {
                if (source != null) {
                    source.close();
                }
                if (destination != null) {
                    destination.close();
                }
            }
        }
        catch (FileNotFoundException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
        catch (IOException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }
    }

}
